View Javadoc
1   /**
2    * 
3    */
4   package edu.jiangxin.apktoolbox.android.i18n;
5   
6   import edu.jiangxin.apktoolbox.swing.extend.EasyPanel;
7   import edu.jiangxin.apktoolbox.swing.extend.listener.SelectDirectoryListener;
8   import edu.jiangxin.apktoolbox.utils.Constants;
9   import org.apache.commons.collections4.map.HashedMap;
10  import org.apache.commons.io.FileUtils;
11  import org.apache.commons.lang3.StringUtils;
12  import org.jdom2.Document;
13  import org.jdom2.Element;
14  import org.jdom2.JDOMException;
15  import org.jdom2.input.SAXBuilder;
16  import org.jdom2.output.Format;
17  import org.jdom2.output.XMLOutputter;
18  
19  import javax.swing.*;
20  import java.awt.*;
21  import java.io.*;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.List;
25  import java.util.Map;
26  
27  /**
28   * @author jiangxin
29   * @author 2019-04-12
30   *
31   */
32  public class I18nAddPanel extends EasyPanel {
33      
34      @Serial
35      private static final long serialVersionUID = 1L;
36      
37      private static final String CHARSET = "UTF-8";
38  
39      private static final boolean REMOVE_LAST_LF_OPEN = true;
40  
41      private static final Map<String, String> replace = new HashedMap<>();
42  
43      private JTextField srcTextField;
44  
45      private JTextField targetTextField;
46  
47      private JTextField itemTextField;
48  
49      private int operationCount = 0;
50  
51      static {
52          replace.put("&quot;", "jiangxin001");
53          replace.put("&#160;", "jiangxin002");
54      }
55      
56      public I18nAddPanel() throws HeadlessException {
57          super();
58      }
59  
60      @Override
61      public void initUI() {
62          BoxLayout boxLayout = new BoxLayout(this, BoxLayout.Y_AXIS);
63          setLayout(boxLayout);
64  
65          createSourcePanel();
66          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
67          createTargetPanel();
68          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
69          createItemPanel();
70          add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER));
71          createOperationPanel();
72      }
73  
74      private void createOperationPanel() {
75          JPanel operationPanel = new JPanel();
76          operationPanel.setLayout(new BoxLayout(operationPanel, BoxLayout.X_AXIS));
77          add(operationPanel);
78  
79          JButton addButton = new JButton(bundle.getString("android.i18n.add.title"));
80          addButton.addActionListener(e -> {
81              String srcPath = checkAndGetDirContent(srcTextField, "android.i18n.add.src.dir", "Source directory is invalid");
82              if (srcPath == null) {
83                  return;
84              }
85  
86              String targetPath = checkAndGetDirContent(targetTextField, "android.i18n.add.target.dir", "Target directory is invalid");
87              if (targetPath == null) {
88                  return;
89              }
90  
91              String itemStr = checkAndGetStringContent(itemTextField, "android.i18n.add.items", "Items is empty");
92              if (itemStr == null) {
93                  return;
94              }
95  
96              List<String> items = new ArrayList<>(Arrays.asList(itemStr.split(";")));
97              operationCount = 0;
98  
99              for (String item : items) {
100                 int ret = innerProcessor(srcPath, targetPath, item);
101                 if (ret != 0) {
102                     Toolkit.getDefaultToolkit().beep();
103                     JOptionPane.showMessageDialog(I18nAddPanel.this, "Failed, please see the log", "ERROR",
104                             JOptionPane.ERROR_MESSAGE);
105                     return;
106                 }
107             }
108             String message = String.format("Success: items: %d", operationCount);
109             JOptionPane.showMessageDialog(I18nAddPanel.this, message, "INFO", JOptionPane.INFORMATION_MESSAGE);
110         });
111 
112         operationPanel.add(addButton);
113     }
114 
115     private void createItemPanel() {
116         JPanel itemPanel = new JPanel();
117         itemPanel.setLayout(new BoxLayout(itemPanel, BoxLayout.X_AXIS));
118         add(itemPanel);
119         
120         itemTextField = new JTextField();
121         itemTextField.setText(conf.getString("android.i18n.add.items"));
122 
123         JLabel itemLabel = new JLabel("Items");
124 
125         itemPanel.add(itemTextField);
126         itemPanel.add(Box.createHorizontalGlue());
127         itemPanel.add(itemLabel);
128     }
129 
130     private void createTargetPanel() {
131         JPanel targetPanel = new JPanel();
132         targetPanel.setLayout(new BoxLayout(targetPanel, BoxLayout.X_AXIS));
133         add(targetPanel);
134         
135         targetTextField = new JTextField();
136         targetTextField.setText(conf.getString("android.i18n.add.target.dir"));
137 
138         JButton targetButton = new JButton("Save Directory");
139         targetButton.addActionListener(new SelectDirectoryListener("save to", targetTextField));
140 
141         targetPanel.add(targetTextField);
142         targetPanel.add(Box.createHorizontalGlue());
143         targetPanel.add(targetButton);
144     }
145 
146     private void createSourcePanel() {
147         JPanel sourcePanel = new JPanel();
148         sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.X_AXIS));
149         add(sourcePanel);
150         
151         srcTextField = new JTextField();
152         srcTextField.setText(conf.getString("android.i18n.add.src.dir"));
153 
154         JButton srcButton = new JButton("Source Directory");
155         srcButton.addActionListener(new SelectDirectoryListener("select a directory", srcTextField));
156 
157         sourcePanel.add(srcTextField);
158         sourcePanel.add(Box.createHorizontalGlue());
159         sourcePanel.add(srcButton);
160     }
161 
162     private int innerProcessor(String sourceBaseStr, String targetBaseStr, String itemName) {
163         if (StringUtils.isAnyEmpty(sourceBaseStr, targetBaseStr, itemName)) {
164             logger.error("params are invalid: sourceBaseStr: {}, targetBaseStr: {}, itemName: {}", sourceBaseStr, targetBaseStr, itemName);
165             return -1;
166         }
167         File sourceBaseFile = new File(sourceBaseStr);
168         File targetBaseFile = new File(targetBaseStr);
169         int count = 0;
170 
171         File[] sourceParentFiles = sourceBaseFile.listFiles(new FileFilter() {
172             @Override
173             public boolean accept(File pathname) {
174                 return pathname.getName().startsWith("values");
175             }
176         });
177         if (sourceParentFiles == null) {
178             logger.error("sourceParentFiles is null");
179             return -1;
180         }
181         for (File sourceParentFile : sourceParentFiles) {
182             File sourceFile = new File(sourceParentFile, "strings.xml");
183 
184             Element sourceElement = getSourceElement(sourceFile, itemName);
185             if (sourceElement == null) {
186                 logger.warn("sourceElement is null: {}", sourceFile);
187                 continue;
188             }
189 
190             File targetFile = new File(new File(targetBaseFile, sourceParentFile.getName()), "strings.xml");
191             if (!targetFile.exists()) {
192                 logger.warn("targetFile does not exist: {}", sourceFile);
193                 continue;
194             }
195             try {
196                 preProcess(targetFile);
197             } catch (IOException e) {
198                 logger.error("preProcess failed.", e);
199                 return -1;
200             }
201             boolean res = setTargetElement(targetFile, sourceElement, itemName);
202             if (!res) {
203                 logger.error("setTargetElement failed.");
204                 return -1;
205             }
206             try {
207                 postProcess(targetFile);
208             } catch (IOException e) {
209                 logger.error("postProcess failed.", e);
210                 return -1;
211             }
212             logger.info("count: {}, in path: {}, out path: {}", ++count, sourceFile, targetFile);
213         }
214         operationCount += count;
215         logger.info("finish one cycle");
216         return 0;
217     }
218 
219     private Element getSourceElement(File sourceFile, String itemName) {
220         if (!sourceFile.exists()) {
221             logger.warn("sourceFile does not exist: {}", sourceFile);
222             return null;
223         }
224         SAXBuilder builder = new SAXBuilder();
225         Document sourceDoc = null;
226         try (InputStream in = new FileInputStream(sourceFile)) {
227             sourceDoc = builder.build(in);
228             logger.info("build source document: {}", sourceFile);
229         } catch (JDOMException | IOException e) {
230             logger.error("build source document failed: {}", sourceFile);
231             return null;
232         }
233         if (sourceDoc == null) {
234             logger.error("sourceDoc is null");
235             return null;
236         }
237         Element sourceElement = null;
238         for (Element sourceChild : sourceDoc.getRootElement().getChildren()) {
239             String sourceValue = sourceChild.getAttributeValue("name");
240             if (sourceValue != null && sourceValue.equals(itemName)) {
241                 sourceElement = sourceChild.clone();
242                 break;
243             }
244         }
245         return sourceElement;
246     }
247 
248     private boolean setTargetElement(File targetFile, Element sourceElement, String itemName) {
249         SAXBuilder builder = new SAXBuilder();
250         Document targetDoc;
251         try {
252             targetDoc = builder.build(targetFile);
253             logger.info("build target document: {}", targetFile);
254         } catch (JDOMException | IOException e) {
255             logger.error("build target document failed: {}", targetFile);
256             return false;
257         }
258         Element targetRoot = targetDoc.getRootElement();
259         boolean isFinished = false;
260         for (Element targetChild : targetRoot.getChildren()) {
261             String targetValue = targetChild.getAttributeValue("name");
262             if (targetValue != null && targetValue.equals(itemName)) {
263                 targetChild.setText(sourceElement.getText());
264                 isFinished = true;
265                 break;
266             }
267         }
268         if (!isFinished) {
269             targetRoot.addContent("    ");
270             targetRoot.addContent(sourceElement);
271             targetRoot.addContent("\n");
272         }
273         XMLOutputter out = new XMLOutputter();
274         Format format = Format.getRawFormat();
275         format.setEncoding("UTF-8");
276         format.setLineSeparator("\n");
277         out.setFormat(format);
278         OutputStream os = null;
279         try {
280             os = new FileOutputStream(targetFile);
281             out.output(targetDoc, os);
282         } catch (IOException e) {
283             logger.error("output fail", e);
284             return false;
285         } finally {
286             if (os != null) {
287                 try {
288                     os.close();
289                 } catch (IOException e) {
290                     logger.error("close output stream exception", e);
291                 }
292             }
293         }
294         return true;
295     }
296 
297     private static void preProcess(File file) throws IOException {
298         String content = FileUtils.readFileToString(file, CHARSET);
299         for (Map.Entry<String, String> entry : replace.entrySet()) {
300             content = content.replaceAll(entry.getKey(), entry.getValue());
301         }
302         FileUtils.writeStringToFile(file, content, CHARSET);
303     }
304 
305     private static void postProcess(File file) throws IOException {
306         String content = FileUtils.readFileToString(file, CHARSET);
307         for (Map.Entry<String, String> entry : replace.entrySet()) {
308             content = content.replaceAll(entry.getValue(), entry.getKey());
309         }
310         if (REMOVE_LAST_LF_OPEN) {
311             content = StringUtils.removeEnd(content, "\n");
312         }
313         FileUtils.writeStringToFile(file, content, CHARSET);
314     }
315 
316 }